// Copyright 2008 David Mitchell
// Released under the MIT License

using System;

namespace NSDuctTape
{
	public class Instance: IDisposable
	{
		public Instance(IntPtr nativeInstance)
		{
			if (nativeInstance == IntPtr.Zero)
				throw new ArgumentException("nativeInstance", "nativeInstance cannot be equal to IntPtr.Zero");
			ObjectiveCMethods.SendMessage(nativeInstance, Selectors.Retain);
			_nativeInstance = nativeInstance;
		}
		
		public Instance(string className)
		{
			_nativeInstance = ObjectiveCMethods.CreateInstance(ObjectiveCMethods.GetClass(className), 0);
			ObjectiveCMethods.SendMessage(_nativeInstance, Selectors.Init);
		}
		
		public Instance(string className, string initSelector, params object[] arguments)
		{
			_nativeInstance = ObjectiveCMethods.CreateInstance(ObjectiveCMethods.GetClass(className), 0);
			new Invocation(_nativeInstance, ObjectiveCMethods.SelectorFromString(initSelector)).Invoke(arguments);
		}
		
		public Instance Copy()
		{
			if (_disposed)
				throw new ObjectDisposedException(ToString());
			return new Instance(_nativeInstance); 
		}
		
		public void Dispose()
		{
			Dispose(true);
		}
		
		public void SendMessage(string selector, params object[] arguments)
		{
			if (_disposed)
				throw new ObjectDisposedException(ToString());
			new Invocation(_nativeInstance, ObjectiveCMethods.SelectorFromString(selector)).Invoke(arguments);
		}
		
		public TResult SendMessage<TResult>(string selector, params object[] arguments)
		{
			if (_disposed)
				throw new ObjectDisposedException(ToString());
			return new Invocation(_nativeInstance, ObjectiveCMethods.SelectorFromString(selector)).Invoke<TResult>(arguments);
		}
		
		protected virtual void Dispose(bool disposing)
		{
			if (!_disposed)
			{
				ObjectiveCMethods.SendMessage(_nativeInstance, Selectors.Release);
				_disposed = true;
				if (disposing)
					GC.SuppressFinalize(this);
			}
		}
		
		internal IntPtr ToIntPtr()
		{
			return _nativeInstance;
		}
		
		~Instance()
		{
			Dispose(false);
		}

		bool _disposed;
		readonly IntPtr _nativeInstance;
	}
}
